home *** CD-ROM | disk | FTP | other *** search
/ Clickx 96 / Clickx 96.iso / software / tools / tool / xbmc-10.1.exe / addons / script.module.pil / lib / PIL / GifImagePlugin.py < prev    next >
Encoding:
Python Source  |  2009-11-15  |  10.8 KB  |  408 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # GIF file handling
  6. #
  7. # History:
  8. # 1995-09-01 fl   Created
  9. # 1996-12-14 fl   Added interlace support
  10. # 1996-12-30 fl   Added animation support
  11. # 1997-01-05 fl   Added write support, fixed local colour map bug
  12. # 1997-02-23 fl   Make sure to load raster data in getdata()
  13. # 1997-07-05 fl   Support external decoder (0.4)
  14. # 1998-07-09 fl   Handle all modes when saving (0.5)
  15. # 1998-07-15 fl   Renamed offset attribute to avoid name clash
  16. # 2001-04-16 fl   Added rewind support (seek to frame 0) (0.6)
  17. # 2001-04-17 fl   Added palette optimization (0.7)
  18. # 2002-06-06 fl   Added transparency support for save (0.8)
  19. # 2004-02-24 fl   Disable interlacing for small images
  20. #
  21. # Copyright (c) 1997-2004 by Secret Labs AB
  22. # Copyright (c) 1995-2004 by Fredrik Lundh
  23. #
  24. # See the README file for information on usage and redistribution.
  25. #
  26.  
  27.  
  28. __version__ = "0.9"
  29.  
  30.  
  31. import Image, ImageFile, ImagePalette
  32.  
  33.  
  34. # --------------------------------------------------------------------
  35. # Helpers
  36.  
  37. def i16(c):
  38.     return ord(c[0]) + (ord(c[1])<<8)
  39.  
  40. def o16(i):
  41.     return chr(i&255) + chr(i>>8&255)
  42.  
  43.  
  44. # --------------------------------------------------------------------
  45. # Identify/read GIF files
  46.  
  47. def _accept(prefix):
  48.     return prefix[:6] in ["GIF87a", "GIF89a"]
  49.  
  50. ##
  51. # Image plugin for GIF images.  This plugin supports both GIF87 and
  52. # GIF89 images.
  53.  
  54. class GifImageFile(ImageFile.ImageFile):
  55.  
  56.     format = "GIF"
  57.     format_description = "Compuserve GIF"
  58.  
  59.     global_palette = None
  60.  
  61.     def data(self):
  62.         s = self.fp.read(1)
  63.         if s and ord(s):
  64.             return self.fp.read(ord(s))
  65.         return None
  66.  
  67.     def _open(self):
  68.  
  69.         # Screen
  70.         s = self.fp.read(13)
  71.         if s[:6] not in ["GIF87a", "GIF89a"]:
  72.             raise SyntaxError, "not a GIF file"
  73.  
  74.         self.info["version"] = s[:6]
  75.  
  76.         self.size = i16(s[6:]), i16(s[8:])
  77.  
  78.         self.tile = []
  79.  
  80.         flags = ord(s[10])
  81.  
  82.         bits = (flags & 7) + 1
  83.  
  84.         if flags & 128:
  85.             # get global palette
  86.             self.info["background"] = ord(s[11])
  87.             # check if palette contains colour indices
  88.             p = self.fp.read(3<<bits)
  89.             for i in range(0, len(p), 3):
  90.                 if not (chr(i/3) == p[i] == p[i+1] == p[i+2]):
  91.                     p = ImagePalette.raw("RGB", p)
  92.                     self.global_palette = self.palette = p
  93.                     break
  94.  
  95.         self.__fp = self.fp # FIXME: hack
  96.         self.__rewind = self.fp.tell()
  97.         self.seek(0) # get ready to read first frame
  98.  
  99.     def seek(self, frame):
  100.  
  101.         if frame == 0:
  102.             # rewind
  103.             self.__offset = 0
  104.             self.dispose = None
  105.             self.__frame = -1
  106.             self.__fp.seek(self.__rewind)
  107.  
  108.         if frame != self.__frame + 1:
  109.             raise ValueError, "cannot seek to frame %d" % frame
  110.         self.__frame = frame
  111.  
  112.         self.tile = []
  113.  
  114.         self.fp = self.__fp
  115.         if self.__offset:
  116.             # backup to last frame
  117.             self.fp.seek(self.__offset)
  118.             while self.data():
  119.                 pass
  120.             self.__offset = 0
  121.  
  122.         if self.dispose:
  123.             self.im = self.dispose
  124.             self.dispose = None
  125.  
  126.         self.palette = self.global_palette
  127.  
  128.         while 1:
  129.  
  130.             s = self.fp.read(1)
  131.             if not s or s == ";":
  132.                 break
  133.  
  134.             elif s == "!":
  135.                 #
  136.                 # extensions
  137.                 #
  138.                 s = self.fp.read(1)
  139.                 block = self.data()
  140.                 if ord(s) == 249:
  141.                     #
  142.                     # graphic control extension
  143.                     #
  144.                     flags = ord(block[0])
  145.                     if flags & 1:
  146.                         self.info["transparency"] = ord(block[3])
  147.                     self.info["duration"] = i16(block[1:3]) * 10
  148.                     try:
  149.                         # disposal methods
  150.                         if flags & 8:
  151.                             # replace with background colour
  152.                             self.dispose = Image.core.fill("P", self.size,
  153.                                 self.info["background"])
  154.                         elif flags & 16:
  155.                             # replace with previous contents
  156.                             self.dispose = self.im.copy()
  157.                     except (AttributeError, KeyError):
  158.                         pass
  159.                 elif ord(s) == 255:
  160.                     #
  161.                     # application extension
  162.                     #
  163.                     self.info["extension"] = block, self.fp.tell()
  164.                     if block[:11] == "NETSCAPE2.0":
  165.                         block = self.data()
  166.                         if len(block) >= 3 and ord(block[0]) == 1:
  167.                             self.info["loop"] = i16(block[1:3])
  168.                 while self.data():
  169.                     pass
  170.  
  171.             elif s == ",":
  172.                 #
  173.                 # local image
  174.                 #
  175.                 s = self.fp.read(9)
  176.  
  177.                 # extent
  178.                 x0, y0 = i16(s[0:]), i16(s[2:])
  179.                 x1, y1 = x0 + i16(s[4:]), y0 + i16(s[6:])
  180.                 flags = ord(s[8])
  181.  
  182.                 interlace = (flags & 64) != 0
  183.  
  184.                 if flags & 128:
  185.                     bits = (flags & 7) + 1
  186.                     self.palette =\
  187.                         ImagePalette.raw("RGB", self.fp.read(3<<bits))
  188.  
  189.                 # image data
  190.                 bits = ord(self.fp.read(1))
  191.                 self.__offset = self.fp.tell()
  192.                 self.tile = [("gif",
  193.                              (x0, y0, x1, y1),
  194.                              self.__offset,
  195.                              (bits, interlace))]
  196.                 break
  197.  
  198.             else:
  199.                 pass
  200.                 # raise IOError, "illegal GIF tag `%x`" % ord(s)
  201.  
  202.         if not self.tile:
  203.             # self.__fp = None
  204.             raise EOFError, "no more images in GIF file"
  205.  
  206.         self.mode = "L"
  207.         if self.palette:
  208.             self.mode = "P"
  209.  
  210.     def tell(self):
  211.         return self.__frame
  212.  
  213.  
  214. # --------------------------------------------------------------------
  215. # Write GIF files
  216.  
  217. try:
  218.     import _imaging_gif
  219. except ImportError:
  220.     _imaging_gif = None
  221.  
  222. RAWMODE = {
  223.     "1": "L",
  224.     "L": "L",
  225.     "P": "P",
  226. }
  227.  
  228. def _save(im, fp, filename):
  229.  
  230.     if _imaging_gif:
  231.         # call external driver
  232.         try:
  233.             _imaging_gif.save(im, fp, filename)
  234.             return
  235.         except IOError:
  236.             pass # write uncompressed file
  237.  
  238.     try:
  239.         rawmode = RAWMODE[im.mode]
  240.         imOut = im
  241.     except KeyError:
  242.         # convert on the fly (EXPERIMENTAL -- I'm not sure PIL
  243.         # should automatically convert images on save...)
  244.         if Image.getmodebase(im.mode) == "RGB":
  245.             imOut = im.convert("P")
  246.             rawmode = "P"
  247.         else:
  248.             imOut = im.convert("L")
  249.             rawmode = "L"
  250.  
  251.     # header
  252.     for s in getheader(imOut, im.encoderinfo):
  253.         fp.write(s)
  254.  
  255.     flags = 0
  256.  
  257.     try:
  258.         interlace = im.encoderinfo["interlace"]
  259.     except KeyError:
  260.         interlace = 1
  261.  
  262.     # workaround for @PIL153
  263.     if min(im.size) < 16:
  264.         interlace = 0
  265.  
  266.     if interlace:
  267.         flags = flags | 64
  268.  
  269.     try:
  270.         transparency = im.encoderinfo["transparency"]
  271.     except KeyError:
  272.         pass
  273.     else:
  274.         # transparency extension block
  275.         fp.write("!" +
  276.                  chr(249) +             # extension intro
  277.                  chr(4) +               # length
  278.                  chr(1) +               # transparency info present
  279.                  o16(0) +               # duration
  280.                  chr(int(transparency)) # transparency index
  281.                  + chr(0))
  282.  
  283.     # local image header
  284.     fp.write("," +
  285.              o16(0) + o16(0) +          # bounding box
  286.              o16(im.size[0]) +          # size
  287.              o16(im.size[1]) +
  288.              chr(flags) +               # flags
  289.              chr(8))                    # bits
  290.  
  291.     imOut.encoderconfig = (8, interlace)
  292.  
  293.     ImageFile._save(imOut, fp, [("gif", (0,0)+im.size, 0, rawmode)])
  294.  
  295.     fp.write("\0") # end of image data
  296.  
  297.     fp.write(";") # end of file
  298.  
  299.     try:
  300.         fp.flush()
  301.     except: pass
  302.  
  303. def _save_netpbm(im, fp, filename):
  304.  
  305.     #
  306.     # If you need real GIF compression and/or RGB quantization, you
  307.     # can use the external NETPBM/PBMPLUS utilities.  See comments
  308.     # below for information on how to enable this.
  309.  
  310.     import os
  311.     file = im._dump()
  312.     if im.mode != "RGB":
  313.         os.system("ppmtogif %s >%s" % (file, filename))
  314.     else:
  315.         os.system("ppmquant 256 %s | ppmtogif >%s" % (file, filename))
  316.     try: os.unlink(file)
  317.     except: pass
  318.  
  319.  
  320. # --------------------------------------------------------------------
  321. # GIF utilities
  322.  
  323. def getheader(im, info=None):
  324.     """Return a list of strings representing a GIF header"""
  325.  
  326.     optimize = info and info.get("optimize", 0)
  327.  
  328.     s = [
  329.         "GIF87a" +              # magic
  330.         o16(im.size[0]) +       # size
  331.         o16(im.size[1]) +
  332.         chr(7 + 128) +          # flags: bits + palette
  333.         chr(0) +                # background
  334.         chr(0)                  # reserved/aspect
  335.     ]
  336.  
  337.     if optimize:
  338.         # minimize color palette
  339.         i = 0
  340.         maxcolor = 0
  341.         for count in im.histogram():
  342.             if count:
  343.                 maxcolor = i
  344.             i = i + 1
  345.     else:
  346.         maxcolor = 256
  347.  
  348.     # global palette
  349.     if im.mode == "P":
  350.         # colour palette
  351.         s.append(im.im.getpalette("RGB")[:maxcolor*3])
  352.     else:
  353.         # greyscale
  354.         for i in range(maxcolor):
  355.             s.append(chr(i) * 3)
  356.  
  357.     return s
  358.  
  359. def getdata(im, offset = (0, 0), **params):
  360.     """Return a list of strings representing this image.
  361.        The first string is a local image header, the rest contains
  362.        encoded image data."""
  363.  
  364.     class collector:
  365.         data = []
  366.         def write(self, data):
  367.             self.data.append(data)
  368.  
  369.     im.load() # make sure raster data is available
  370.  
  371.     fp = collector()
  372.  
  373.     try:
  374.         im.encoderinfo = params
  375.  
  376.         # local image header
  377.         fp.write("," +
  378.                  o16(offset[0]) +       # offset
  379.                  o16(offset[1]) +
  380.                  o16(im.size[0]) +      # size
  381.                  o16(im.size[1]) +
  382.                  chr(0) +               # flags
  383.                  chr(8))                # bits
  384.  
  385.         ImageFile._save(im, fp, [("gif", (0,0)+im.size, 0, RAWMODE[im.mode])])
  386.  
  387.         fp.write("\0") # end of image data
  388.  
  389.     finally:
  390.         del im.encoderinfo
  391.  
  392.     return fp.data
  393.  
  394.  
  395. # --------------------------------------------------------------------
  396. # Registry
  397.  
  398. Image.register_open(GifImageFile.format, GifImageFile, _accept)
  399. Image.register_save(GifImageFile.format, _save)
  400. Image.register_extension(GifImageFile.format, ".gif")
  401. Image.register_mime(GifImageFile.format, "image/gif")
  402.  
  403. #
  404. # Uncomment the following line if you wish to use NETPBM/PBMPLUS
  405. # instead of the built-in "uncompressed" GIF encoder
  406.  
  407. # Image.register_save(GifImageFile.format, _save_netpbm)
  408.